/**
 * 闭包即匿名函数
 * 为了方便统一，这里称为匿名函数
 * 
 * 一、基本定义
 * 闭包即匿名函数，特定：
 * a.函数/方法体内
 * b.对于其入参和返回通常不需要做显式的类型限定；如果要做也可以；某些情况下必须限定
 * c.匿名函数的格式比较怪异，非得不同于其它语言，大概格式如下:
 *    s1.|参数列表| -> 返回值类型 { 函数体 }
 * 
 * 二、定义形式（书写形式)
 * a.用变量绑定匿名函数
 * let 变量名 = |参数列表| -> 返回值类型 { 函数体 };
 * let f = |x| -> u32 { x+1};
 * let f=|x:u32| -> u32 { x+1};
 * let f=|x|->x+1;    //这个不能当做具有通用类型的函数使用
 * 
 * b.直接使用匿名函数，通常是作为参数
 * |参数列表| -> 返回值类型 { 函数体 }
 * c.直接使用匿名函数，通常作为返回值
 * 
 * 如果不在乎编译时间，也不在乎可读性，那么可以不限定类型。但是我个人推荐使用限定类型，这样更容易理解代码，编译的速度也会更快一些。
 * 
 * 三、类型推断
 * 匿名函数的参数列表和返回值类型可以省略，编译器会根据上下文自动推断。
 * 但是，这并不意味着可以传递任意参数：如果使用一个匿名函数在一个程序主体中传递不同类型的参数，将导致编译错误。
 * 也就是说，在没有特别处理的情况下,编译器会根据某个使用情况，把匿名函数编译为特定的方法（指定了类型）。
 * 用户不能把没有特别处理的匿名函数当做具有通用类型的函数/方法来使用的。
 *     let example_closure = |x| x;
 *     let s = example_closure(String::from("hello"));
 *     let n = example_closure(5);
 * 这个例子会导致编译错误，因为编译器已经通过第一次执行推断类型必须是String.
 * 
 * 四、变量问题
 * 1.捕获（不可变引用、可变引用、移动值（所有权转移）)
 * 2.移出捕获的变量-即如何把捕获的变量移出匿名函数体。
 * 
 * 第二个问题和Fn特质有关，有三种Fn特质：FnOnce、FnMut和Fn。
 * FnOnce 只能被调用一次，FnMut 可以被多次调用，Fn 不能被调用两次。
 * 
 * 
 * 五、优缺点
 * 
 * 六、如何实现的？
 * 说的是编译器如何实现这个功能？
 */
#[derive(Debug, PartialEq, Copy, Clone)]
enum ShirtColor {红,橙,黄,绿,青,蓝,紫,黑,白}
struct InvCabinet{
    color:ShirtColor,
    qty:u32
}

struct Inventory{
    shirts:Vec<InvCabinet>
}

impl Inventory {
    fn  giveaway(&mut self, user_prefer:Option<ShirtColor>) -> ShirtColor {
        user_prefer.unwrap_or_else(|| self.most_stocked())
    }
    /**
     * 返回库存最多的颜色
     */
    fn most_stocked(&self) -> ShirtColor {
        let mut max_qty=self.shirts.get(0).unwrap().qty;
        let mut idx=0;
        for i in 1..self.shirts.len() {
            if self.shirts[i].qty > max_qty {
                max_qty = self.shirts[i].qty;
                idx = i;
            }
        }
        self.shirts[idx].color        
    }
}

fn main() {
     let mao_color=Some(ShirtColor::白);
     let me_color  = None;

     let mut inventory = Inventory {
         shirts: vec![
             InvCabinet { color: ShirtColor::白, qty: 10 },
             InvCabinet { color: ShirtColor::黄, qty: 20 },
             InvCabinet { color: ShirtColor::红, qty: 30 }],
     };
     println!("用户{:?}获赠{:?}", mao_color,inventory.giveaway(mao_color));
     println!("用户{:?}获赠{:?}", me_color,inventory.giveaway(me_color));
}